package com.atguigu.gmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.bean.PageParamVo;
import com.atguigu.gmall.common.bean.ResponseVo;
import com.atguigu.gmall.pms.entity.*;
import com.atguigu.gmall.search.entity.*;
import com.atguigu.gmall.search.feign.GmallPmsClient;
import com.atguigu.gmall.search.service.SearchService;
import com.atguigu.gmall.wms.api.GmallWmsApi;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    RestHighLevelClient restHighLevelClient;

    @Autowired
    GmallPmsClient gmallPmsClient;
    @Autowired
    GmallWmsApi gmallWmsApi;

 @Autowired
    ElasticsearchRestTemplate restTemplate;

    /**
     * 实现搜索商品的功能
     * @param searchParamVo
     * @return
     */
    @Override
    public SearchResponseVo search(SearchParamVo searchParamVo) {
        SearchResponseVo searchResponseVo = null;
        try {
            SearchResponse response = restHighLevelClient.search(new SearchRequest(new String[]{"goods"}, this.searchSourceBuilder(searchParamVo)), RequestOptions.DEFAULT);
             searchResponseVo = this.parseSearchResponse(response);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return searchResponseVo;
    }

    /**
     * 新增es对应spu的索引
     * @param id
     */
    @Override
    public void createIndex(Long id) {
        //根据spu的id查询对应的spu的信息及sku的信息
        ResponseVo<SpuEntity> spuEntityResponseVo = gmallPmsClient.querySpuById(id);
        SpuEntity spu = spuEntityResponseVo.getData();
        ResponseVo<List<SkuEntity>> skusResponse = gmallPmsClient.querySkuBySpuId(id);
        List<SkuEntity> skus = skusResponse.getData();
        //设置品牌聚合字段
        ResponseVo<BrandEntity> brandEntityResponseVo = gmallPmsClient.queryBrandById(spu.getBrandId());
        BrandEntity brandEntity = brandEntityResponseVo.getData();
        //查询分类相关字段
        ResponseVo<CategoryEntity> categoryEntityResponseVo = gmallPmsClient.queryCategoryById(spu.getCategoryId());
        CategoryEntity categoryEntity = categoryEntityResponseVo.getData();

        //查询spu规格参数销售相关的字段
        ResponseVo<List<SpuAttrValueEntity>> SpuAttrValuelistResponseVo = gmallPmsClient.querySearchAttrValueByCidAndSpuId(spu.getCategoryId(), spu.getId());
        List<SpuAttrValueEntity> spuAttrValueEntities = SpuAttrValuelistResponseVo.getData();
        //3、遍历sku集合，并将每个sku的参数设置到good中，转换为goods集合，存储到es中
        if (!CollectionUtils.isEmpty(skus)){
            List<Goods> goods = skus.stream().map(sku -> {
                Goods good = new Goods();
                good.setSkuId(sku.getId());
                good.setTitle(sku.getTitle());
                good.setSubtitle(sku.getSubtitle());
                good.setDefaultImage(sku.getDefaultImage());
                good.setCreateTime(spu.getCreateTime());
                good.setPrice(sku.getPrice().doubleValue());
                ResponseVo<List<WareSkuEntity>> listResponseWmsVo = gmallWmsApi.queryWareSkuBySkuId(sku.getId());
                List<WareSkuEntity> wareSkuEntityList = listResponseWmsVo.getData();
                if (!CollectionUtils.isEmpty(wareSkuEntityList)){
                    good.setSales(wareSkuEntityList.stream().map(WareSkuEntity::getSales).reduce((a,b)->a+b).get());
                    good.setStore(wareSkuEntityList.stream().anyMatch(wareSkuentity->wareSkuentity.getStockLocked()-wareSkuentity.getStock()<0));
                }
                if (brandEntity!=null){
                    good.setBrandId(brandEntity.getId());
                    good.setBrandName(brandEntity.getName());
                    good.setLogo(brandEntity.getLogo());
                }
                //设置分类聚合字段
                if (categoryEntity!=null){
                    good.setCategoryId(categoryEntity.getId());
                    good.setCategoryName(categoryEntity.getName());
                }

                //设置查询规格参数
                List<SearchAttrValue> searchAttrs = new ArrayList<>();
                ResponseVo<List<SkuAttrValueEntity>> SkuAttrValueEntitylistResponseVo = gmallPmsClient.querySearchAttrValueByCidAndSkuId(spu.getCategoryId(), sku.getId());
                List<SkuAttrValueEntity> skuAttrList = SkuAttrValueEntitylistResponseVo.getData();
                if (!CollectionUtils.isEmpty(skuAttrList)){

                    searchAttrs.addAll(skuAttrList.stream().map(skuAttr->{
                        SearchAttrValue searchAttrValue = new SearchAttrValue();
                        BeanUtils.copyProperties(skuAttr,searchAttrValue);
                        return searchAttrValue;
                    }).collect(Collectors.toList()));
                }
                if (!CollectionUtils.isEmpty(spuAttrValueEntities)){
                    searchAttrs.addAll(spuAttrValueEntities.stream().map(spuAttrEntities->{
                        SearchAttrValue searchAttrValue = new SearchAttrValue();
                        BeanUtils.copyProperties(spuAttrEntities,searchAttrValue);
                        return searchAttrValue;
                    }).collect(Collectors.toList()));
                }
                good.setSearchAttrs(searchAttrs);
                return good;
            }).collect(Collectors.toList());
            restTemplate.save(goods);
        }



    }

    /**
     * 构建搜索功能的条件
     * @return
     */
    private SearchSourceBuilder searchSourceBuilder(SearchParamVo searchParamVo){
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //1、组合查询，匹配查询，过滤
        String keyword = searchParamVo.getKeyword();
        if (StringUtils.isEmpty(keyword)){
            throw new RuntimeException("搜索条件不能为空");
        }
        //1.1关键字过滤条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.matchQuery("title",keyword).operator(Operator.AND));
        //1.2品牌过滤条件
        List<Long> brandIds = searchParamVo.getBrandId();
        if (!CollectionUtils.isEmpty(brandIds)){
        boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",brandIds));
        }
        //1.3分类过滤条件
        List<Long> categoryIds = searchParamVo.getCategoryId();
        if (!CollectionUtils.isEmpty(categoryIds)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("categoryId",categoryIds));
        }
        //1.4价格范围过滤
        Double priceFrom = searchParamVo.getPriceFrom();
        Double priceTo = searchParamVo.getPriceTo();
        if (priceFrom!=null||priceTo!=null){
            RangeQueryBuilder builder = QueryBuilders.rangeQuery("price");
            if (priceFrom!=null){
                builder.gt(priceFrom);
            }
            if (priceTo!=null){
                builder.lt(priceTo);
            }
            boolQueryBuilder.filter(builder);
        }
        //1.5是否有货
        Boolean store = searchParamVo.isStore();
        if (store!=null){
        boolQueryBuilder.filter(QueryBuilders.termQuery("store",store));
        }
        //1.6规格参数过滤
        List<String> prop = searchParamVo.getProp();
        if (!CollectionUtils.isEmpty(prop)){
            for (String p : prop) {
                String[] split = p.split(":");
                if (split!=null&&split.length==2&&NumberUtils.isCreatable(split[0])){
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                    boolQuery.must(QueryBuilders.termQuery("searchAttrs.attrId",split[0]));
                    boolQuery.must(QueryBuilders.termsQuery("searchAttrs.attrValue",split[1].split("-")));
                    boolQueryBuilder.filter(QueryBuilders.nestedQuery("searchAttrs",boolQuery, ScoreMode.None));
                }
            }
        }
        //2、排序
        Integer sort = searchParamVo.getSort();
        switch (sort){//排序字段过滤,0表示得分排序，1表示价格降序，2、价格升序，3、销量降序，4、新品降序
            case 1: sourceBuilder.sort("price", SortOrder.DESC);break;
            case 2: sourceBuilder.sort("price", SortOrder.ASC);break;
            case 3: sourceBuilder.sort("sales", SortOrder.DESC);break;
            case 4: sourceBuilder.sort("createTime", SortOrder.DESC);break;
            default:
                sourceBuilder.sort("_score",SortOrder.DESC);
        }

        //3、分页
        Integer pageNum = searchParamVo.getPageNum();
        Integer pageSize = searchParamVo.getPageSize();
        sourceBuilder.size(pageSize);
        sourceBuilder.from((pageNum-1)*pageSize);
        //4、高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<font style='color:red;'>");
        highlightBuilder.postTags("</font>");
        sourceBuilder.highlighter(highlightBuilder);

        //5、聚合
            //5.1品牌聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("brandAggs").field("brandId")
                .subAggregation(AggregationBuilders.terms("brandNameAggs").field("brandName"))
                .subAggregation(AggregationBuilders.terms("logoAgg").field("logo")));
            //5.2分类聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryIdAgg").field("categoryId")
                .subAggregation(AggregationBuilders.terms("categoryNameAgg").field("categoryName")));

            //5.3规格参数聚合
        sourceBuilder.aggregation(AggregationBuilders.nested("attrAgg","searchAttrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("searchAttrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("searchAttrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("searchAttrs.attrValue"))));
        sourceBuilder.query(boolQueryBuilder);
        System.out.println(sourceBuilder);
        return sourceBuilder;
    }

    /**
     * 查询结果集解析
     */
    private SearchResponseVo parseSearchResponse(SearchResponse response){
        SearchResponseVo responseVo = new SearchResponseVo();
        SearchHits hits = response.getHits();
        responseVo.setTotal(hits.getTotalHits().value);//设置查询总数
        //解析商品信息，将查询的json反序列化为good商品
        SearchHit[] searchHit = hits.getHits();
        if (searchHit!=null && searchHit.length>0){
            List<Goods> goodsList = Arrays.stream(searchHit).map(hit -> {
                String source = hit.getSourceAsString();
                Goods goods = JSON.parseObject(source, Goods.class);
                //解析高亮的title替换good中的字段
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField title = highlightFields.get("title");
                goods.setTitle(title.getFragments()[0].string());
                return goods;
            }).collect(Collectors.toList());
            responseVo.setGoodsList(goodsList);
        }
        //解析聚合结果集
            //解析品牌聚合结果集
        Aggregations aggregations = response.getAggregations();
        ParsedLongTerms brandAggs = aggregations.get("brandAggs");
        List<? extends Terms.Bucket> brandIdAggsbuckets = brandAggs.getBuckets();
        if (!CollectionUtils.isEmpty(brandIdAggsbuckets)){
            responseVo.setBrands(brandIdAggsbuckets.stream().map(bukets->{
                BrandEntity brandEntity = new BrandEntity();
               brandEntity.setId( ((Terms.Bucket) bukets).getKeyAsNumber().longValue());
               //获取品牌桶中的品牌名称
                ParsedStringTerms brandNameAggsBucket = ((Terms.Bucket) bukets).getAggregations().get("brandNameAggs");
                List<? extends Terms.Bucket> buckets = brandNameAggsBucket.getBuckets();
                if (!CollectionUtils.isEmpty(buckets)){
                    brandEntity.setName(buckets.get(0).getKeyAsString());
                }
                //获取品牌桶中的品牌logo
                ParsedStringTerms logoAgg = ((Terms.Bucket) bukets).getAggregations().get("logoAgg");
                List<? extends Terms.Bucket> logoAggbucketss = logoAgg.getBuckets();
                if (!CollectionUtils.isEmpty(logoAggbucketss)){
                    brandEntity.setLogo(logoAggbucketss.get(0).getKeyAsString());
                }
                return brandEntity;
            }).collect(Collectors.toList()));
        }

            //解析规格参数结果集
        ParsedNested attrAgg  =  aggregations.get("attrAgg");
       ParsedLongTerms attrIdAgg =attrAgg.getAggregations().get("attrIdAgg");
        List<? extends Terms.Bucket> attrIdAggBuckets = attrIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(attrIdAggBuckets)){
            responseVo.setFilters(attrIdAggBuckets.stream().map(bucket->{
                SearchResponseAttrValue searchResponseAttrValue = new SearchResponseAttrValue();
                searchResponseAttrValue.setAttrId(((Terms.Bucket) bucket).getKeyAsNumber().longValue());
                //解析规格参数的名称
                ParsedStringTerms attrNameAgg = ((Terms.Bucket) bucket).getAggregations().get("attrNameAgg");
                List<? extends Terms.Bucket> attrNameAggbuckets = attrNameAgg.getBuckets();
                if (!CollectionUtils.isEmpty(attrNameAggbuckets)){
                    searchResponseAttrValue.setAttrName(attrNameAggbuckets.get(0).getKeyAsString());
                }
                //解析规格参数的值
                ParsedStringTerms   attrValueAgg  = ((Terms.Bucket) bucket).getAggregations().get("attrValueAgg");
                List<? extends Terms.Bucket> attrValueAggBuckets = attrValueAgg.getBuckets();
                if (!CollectionUtils.isEmpty(attrValueAggBuckets)){
                    searchResponseAttrValue.setAttrValues(attrValueAggBuckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList()));
                }
                return searchResponseAttrValue;
            }).collect(Collectors.toList()));
        }

            //解析分类参数结果集
        ParsedLongTerms categoryIdAgg =  aggregations.get("categoryIdAgg");
        List<? extends Terms.Bucket> categoryIdAggBuckets = categoryIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(categoryIdAggBuckets)){
            responseVo.setCategories(categoryIdAggBuckets.stream().map(bucket->{
                CategoryEntity categoryEntity = new CategoryEntity();
                categoryEntity.setId(((Terms.Bucket) bucket).getKeyAsNumber().longValue());
                //解析分类结果集的分类名称
            ParsedStringTerms  categoryNameAgg =  ((Terms.Bucket) bucket).getAggregations().get("categoryNameAgg");
                List<? extends Terms.Bucket> categoryNameAggBuckets = categoryNameAgg.getBuckets();
                if (!CollectionUtils.isEmpty(categoryNameAggBuckets)){
                   categoryEntity.setName(categoryNameAggBuckets.get(0).getKeyAsString());
                }
                return categoryEntity;
            }).collect(Collectors.toList()));
        }
        return responseVo;
    }

}
